<?php

/* 页面框架类 by zengluliu <njuxiahan@gmail.com> */

require_library('framework.Template');
require_library('framework.PageUtil');

class Page extends Template
{
    /**
     * 当前显示页面的名字
     */
    private $pageName = '';

    /**
     * 当前使用的模板的名字
     */
    private $themeName = '';

    /**
     * 当前页面是否处于编辑模式
     */
    private $editMode = false;

    /**
     * 当前页面所在的应用
     */
    private $app = '';
    private $appId = 0;

    /**
     * 当前页面所在的项目
     * $app 和 $project 都是为了定位页面的模板文件
     */
    private $project = '';

    /**
     * 页面的路径,临时这样处理的
     */
    private $pagePath = '';

    /**
     * 页面个性因子
     * 比如不同的文章内容页的页面"个性"不同
     * 页面个性因子是一个数组的哈希
     */
    private $styleFactor = 0;

    /**
     * 存储页面内的容器的内容
     */
    private $containers = array();

    /**
     * 本次会话请求的所有页面的默认内容
     */
    private static $entireContainerSettings = array();

    /**
     * 页面和 widget 共享的数据
     */
    private $shareContext = array();

    /**
     * 页面的标题
     */
    private $titles = array();

    /**
     * 页面的 meta 信息
     */
    private $meta = array();

    /**
     * 页面用到的 widget
     */
    public $usedWidgets = array();

    const DEFAULT_STYLE_FACTOR = 0;

    const PAGE_TEMPLATE_SUFFIX = '.phtml';

    public function __construct()
    {
        $args  = func_get_args();
        switch (count($args))
        {
            case 0:
                $themeName = '';
                $editMode = false;
                $app = APP;
                $project = PROJECT;
                break;

            case 2:
                $themeName = $args[0];
                $editMode = $args[1];
                $app = APP;
                $project = PROJECT;
                break;

            case 3:
                $this->construct($args[0], $args[1], $args[2]);
                $themeName = $args[0];
                $editMode = $args[1];
                $app = $args[2];
                $project = PROJECT;
                break;

            case 4:
                $themeName = $args[0];
                $editMode = $args[1];
                $app = $args[2];
                $project = $args[3];
                break;

            default:
                throw new cls_exception('invalid_args');
        }

        $this->themeName = $themeName;
        $this->editMode  = $editMode;
        $this->project   = $project;
        $this->app       = $app;
        $this->appId     = get_app_id($app, $project);
        $this->pagePath  = PageUtil::getPagePath($this->themeName, $this->app, $this->project);

        if ($this->editMode === true)
        {
            start_sessions();
        }

        parent::__construct();
    }

    public function fetch($page, $contentType = Template::DEFAULT_CONTENT_TYPE)
    {
        if (empty($page) || empty($contentType))
        {
            throw new cls_exception('invalid_args');
        }

        $this->init($page, $contentType);

        return $this->fetchContent();
    }

    public function show($page, $contentType = Template::DEFAULT_CONTENT_TYPE)
    {
        if (empty($page) || empty($contentType))
        {
            throw new cls_exception('invalid_args');
        }

        $this->init($page, $contentType);

        $this->display();
    }

    /**
     * 导入一个子页面,子页面一般是页眉或者页脚
     * 子页面不能被拖拽,但是内部的 widget 可以编辑
     */
    public function import($fullPageName)
    {
        $parts  = explode('.', $fullPageName);
        switch (count($parts))
        {
            case 1:
                $subPageName = $fullPageName;
                $sub = new Page($this->themeName, $this->editMode, $this->app, $this->project);
                $configPath = PageUtil::getPagePath($this->themeName, $this->app, $this->project);
                break;

            case 2:
                list($app, $subPageName) = $parts;
                $sub = new Page($this->themeName, $this->editMode, $app, $this->project);
                $configPath = PageUtil::getPagePath($this->themeName, $app, $this->project);
                break;

            default:
                throw new cls_exception('invalid_sub_page_name@import');
        }

        self::$entireContainerSettings[$subPageName] = PageUtil::buildContainerDefaultSettings($configPath, $subPageName);
        if ($this->editMode === true && PageUtil::isFooter($subPageName))
        {
            $containerSettings = array();
            foreach (self::$entireContainerSettings as $settings)
            {
                foreach ($settings as $val)
                {
                    $containerSettings[] = array($val['name'], $val['page'], $val['allowedWidgets'], $val['editable']);
                }
            }

            $alteredWidgets = array();
            foreach (array_keys(self::$entireContainerSettings) as $page)
            {
                $arrayKey = $page . '_' . $this->styleFactor;
                if (!empty($GLOBALS['_SESSION']->PageSettings['altered'][$arrayKey]))
                {
                    $alteredWidgets = array_merge($alteredWidgets, $GLOBALS['_SESSION']->PageSettings['altered'][$arrayKey]);
                }
            }

            $vars = array(
                'containerSettings' => addslashes(json_encode($containerSettings)),
                'editMode' => $this->editMode,
                'editorScripts' => $this->editorScripts(),
            );

            if (empty($alteredWidgets))
            {
                $vars['alteredWidgets'] = addslashes(json_encode(array()));
                $vars['pageAltered'] = false;
            }
            else
            {
                $vars['alteredWidgets'] = addslashes(json_encode(array_values($alteredWidgets)));
                $vars['pageAltered'] = true;
            }
            $sub->assign($vars);
        }

        foreach ($this->titles as $v)
        {
            if (empty($v['titleKey']) || empty($v['option']))
            {
                continue;
            }
            $sub->setTitle($v['titleKey'], $v['option']);
        }

        $sub->assign($this->getTemplateVars());
        $sub->setContext($this->shareContext);
        $sub->setMeta($this->meta);
        $sub->setAllEchoPolicyOff();
        $sub->show($subPageName);
    }

    /**
     * 输出指定的widget的内容
     *
     * @param string $name
     */
    public function container($containerName)
    {
        if (isset($this->containers[$containerName]))
        {
            echo implode('', $this->containers[$containerName]);
            /* 保持行号不变，可以在最后统一去掉
            foreach ($this->containers[$containerName] as $widgetContent){echo $widgetContent;}
            */
        }
    }

    public function getWidgetInstance($widgetId)
    {
        require_module('framework.PageModule');
        if (is_numeric($widgetId))
        {
            $row = PageModule::getWidgetSettings($widgetId);
            if (empty($row))
            {
                throw new cls_exception('invalid_args');
            }

            $containerSettings = PageUtil::buildContainerDefaultSettings(PageUtil::getPagePath($this->themeName, $this->app, $this->project), $this->pageName);
            $setting = isset($containerSettings[$this->pageName][$row['container']]['widgetSetting'][$row['widget']]) ? $containerSettings[$this->pageName][$row['container']]['widgetSetting'][$row['widget']] : array();
            require_library('framework.Widget');
            return Widget::loadWidget($row['widget'], $row['widgetId'], $this, $row['config'], (isset($setting) ? $setting : array()));
        }

        return $this->getWidgetInstance0($widgetId);
    }

    private function getWidgetInstance0($widgetId)
    {
        $settings = $this->buildContainersForView();
        foreach ($settings as $setting)
        {
            if ($setting['widgetId'] === $widgetId)
            {
                $config = unserialize($setting['config']);
                array_walk_recursive($config, 'stripslashes_deep');

                require_library('framework.Widget');
                return Widget::loadWidget($setting['widget'], $setting['widgetId'], $this, $config, (isset($setting['setting']) ? $setting['setting'] : array()));
            }
        }

        return NULL;
    }

    public function getStaticFileURL($styleFileName)
    {
        return $this->templateGetStaticFileURL($this->project, $this->app, $this->themeName, $styleFileName);
    }

    public function outputStyleURL($styleFileName)
    {
        $this->templateOutputStaticFileURL($this->project, $this->app, $this->themeName, $styleFileName);
    }

    public function getEditMode()
    {
        return $this->editMode;
    }

    public function getPageName()
    {
        return $this->pageName;
    }

    /**
     * 获得页面中所有容器的内容
     *
     */
    private function buildContainersForView()
    {
        $settings = $this->getPageSettings();
        $defaultPageSettings = $this->getDefaultPageSettings();
        if (empty($settings))
        {
            $settings = $defaultPageSettings;
        }
        else
        {
            foreach ($settings as $k => $v)
            {
                if (!empty(self::$entireContainerSettings[$this->pageName][$v['container']]))
                {
                    foreach (self::$entireContainerSettings[$this->pageName][$v['container']]['allowedWidgets'] as $widget)
                    {
                        if ($v['widget'] != $widget)
                        {
                            continue;
                        }

                        if (isset(self::$entireContainerSettings[$this->pageName][$v['container']]['widgetCacheSetting'][$widget]))
                        {
                            $settings[$k]['cache'] = self::$entireContainerSettings[$this->pageName][$v['container']]['widgetCacheSetting'][$widget];
                        }

                        if (isset(self::$entireContainerSettings[$this->pageName][$v['container']]['widgetSetting'][$widget]))
                        {
                            $settings[$k]['setting'] = self::$entireContainerSettings[$this->pageName][$v['container']]['widgetSetting'][$widget];
                        }
                    }
                }
            }
        }

        return $settings;
    }

    private function buildContainersForEdit()
    {
        $arrayKey = $this->pageName . '_' . $this->styleFactor;
        if (!PageUtil::isPageSettingsLoaded($arrayKey))
        {
            $settings = $this->getPageSettings();
            $defaultPageSettings = $this->getDefaultPageSettings();
            if (empty($settings))
            {
                $settings = $defaultPageSettings;
            }
            else
            {
                foreach ($settings as $k => $v)
                {
                    if (!empty(self::$entireContainerSettings[$this->pageName][$v['container']]))
                    {
                        foreach (self::$entireContainerSettings[$this->pageName][$v['container']]['allowedWidgets'] as $widget)
                        {
                            if ($v['widget'] != $widget)
                            {
                                continue;
                            }

                            if (isset(self::$entireContainerSettings[$this->pageName][$v['container']]['widgetSetting'][$widget]))
                            {
                                $settings[$k]['setting'] = self::$entireContainerSettings[$this->pageName][$v['container']]['widgetSetting'][$widget];
                            }
                        }
                    }
                }
            }

            if ($this->styleFactor === 0)
            {
                $GLOBALS['_SESSION']->PageSettings['modify'][$arrayKey] = $settings;
            }
            else
            {
                /**
                 * 正在编辑"有分类"的页面
                 */
                foreach ($settings as $val)
                {
                    if ($val['styleFactor'])
                    {
                        $GLOBALS['_SESSION']->PageSettings['modify'][$arrayKey][$val['widgetId']] = $val;
                    }
                    else
                    {
                        // 如果当前记录没有 styleFactor 信息,则拷贝一份这样的数据
                        $widgetId = $this->genRandId();
                        $val['widgetId'] = $widgetId;
                        $val['styleFactor'] = $this->styleFactor;
                        $GLOBALS['_SESSION']->PageSettings['modify'][$arrayKey][$widgetId] = $val;
                    }
                }
            }
        }

        foreach ($GLOBALS['_SESSION']->PageSettings['modify'][$arrayKey] as $val)
        {
            $this->usedWidgets[$val['widget']] = $val['widget'];
        }

        return $GLOBALS['_SESSION']->PageSettings['modify'][$arrayKey];
    }

    private function fillContainers($settings)
    {
        $widgetNames = array();
        foreach ($settings as $val)
        {
            $widgetNames[$val['widget']] = substr($val['widget'], strrpos($val['widget'], '.') + 1);
        }

        if (PROJECT === 'cafe')
        {
            require_module('cafe.service');
            $widgetNames = Service::showWidgetFilter($widgetNames);
        }

        foreach ($settings as $val)
        {
            if (array_key_exists($val['widget'], $widgetNames))
            {
                $this->containers[$val['container']][] = $this->getWidgetView($val);
            }
        }
    }

    /**
     * 从数据库出当前页面的所有设置
     *
     * @return array
     */
    private function getPageSettings($project = PROJECT, $siteId = SITEID, $app = APP)
    {
        $appId = get_app_id($app);
        if ($project === 'eshequn')
        {
            return array();
        }

        $commonWhere = array(
            'project'     => $project,
            'siteId'      => $siteId,
            'appId'       => $appId,
            'page'        => $this->pageName,
            'themeName'   => $this->themeName,
        );

        $where = $commonWhere;
        $where['styleFactor'] = $this->styleFactor;

        require_module('framework.PageModule');
        $result = PageModule::get($where);
        if (empty($result) && $this->styleFactor !== self::DEFAULT_STYLE_FACTOR)
        {
            $where = $commonWhere;
            $where['styleFactor'] = self::DEFAULT_STYLE_FACTOR;
            $result = PageModule::get($where);
        }

        return $result;
    }

    private function getDefaultPageSettings()
    {
        $settings = array();
        $containerNames = array_keys(self::$entireContainerSettings[$this->pageName]);
        if (!empty($containerNames))
        {
            require_library('framework.Widget');
            foreach ($containerNames as $containerName)
            {
                $order = 1;
                foreach (self::$entireContainerSettings[$this->pageName][$containerName]['defaultWidgets'] as $widgetFullName)
                {
                    Widget::requireWidget($widgetFullName);
                    $parseResult = Widget::parseWidgetFullName($widgetFullName);
                    $widgetPageSetting = isset(self::$entireContainerSettings[$this->pageName][$containerName]['widgetSetting'][$widgetFullName]) ? self::$entireContainerSettings[$this->pageName][$containerName]['widgetSetting'][$widgetFullName] : array();
                    $setting = call_user_func(array($parseResult[count($parseResult) - 1], 'buildDefaultSettings'), $widgetPageSetting);
                    if ($this->editMode)
                    {
                        $widgetId = $this->genRandId();
                    }
                    else
                    {
                        $params = array(
                            'siteId'        => SITEID,
                            'project'       => $this->project,
                            'appId'         => $this->appId,
                            'themeName'     => $this->themeName,
                            'page'          => $this->pageName,
                            'container'     => $containerName,
                            'widget'        => $widgetFullName,
                            'number'        => $order
                        );
                        $widgetId = $this->genRandId($params);
                    }
                    $settings[$widgetId] = array(
                        'widgetId'      => $widgetId,
                        'siteId'        => SITEID,
                        'project'       => $this->project,
                        'appId'         => $this->appId,
                        'themeName'     => $this->themeName,
                        'page'          => $this->pageName,
                        'styleFactor'   => $this->styleFactor,
                        'container'     => $containerName,
                        'widget'        => $widgetFullName,
                        'number'        => $order++,
                        'config'        => serialize($setting),
                        'setting'       => $widgetPageSetting,
                        'cache'         => isset(self::$entireContainerSettings[$this->pageName][$containerName]['widgetCacheSetting'][$widgetFullName]) ? self::$entireContainerSettings[$this->pageName][$containerName]['widgetCacheSetting'][$widgetFullName] : array()
                    );
                }
            }
        }

        return $settings;
    }

    /**
     * 根据配置信息获得Widget在指定页面展示的内容
     *
     * @param int $widgetId
     * @param string $widgetName
     * @param array $widgetConfig
     * @return string
     */
    private function getWidgetView($setting)
    {
        $config = unserialize($setting['config']);
        array_walk_recursive($config, 'stripslashes_deep');

        require_library('framework.Widget');
        $widgetObject = Widget::loadWidget($setting['widget'], $setting['widgetId'], $this, $config, (isset($setting['setting']) ? $setting['setting'] : array()));
        $widgetObject->setEditMode($this->editMode);

        if ($this->editMode)
        {
            $widgetId = $setting['widgetId'];
            $widgetFullName = $setting['widget'];
            $editable = $widgetObject->editable();
            $editorTitle = $widgetObject->getEditorTitle();
            $editorWidth = $widgetObject->getEditorWidth();
            $view = $widgetObject->getView();
            $title = $widgetObject->getTitle();

            return <<<EOF
<div widgetName="{$widgetFullName}" widget="{$widgetId}" style="border:none; padding:0; margin:0;overflow:hidden;"
page="{$this->pageName}" title="{$title}" editable="{$editable}" editorTitle="{$editorTitle}" editorWidth="{$editorWidth}">
{$view}
</div>
EOF;
        }
        else
        {
            return self::getWidgetView0($widgetObject, $setting);
        }
    }

    private static function getWidgetView0($widgetObject, $setting)
    {
        if (isset($setting['cache']['type']) && isset($setting['cache']['time']) && $setting['cache']['type'] == 'xc')
        {
            $prefix = PageUtil::getCacheKeyPrefix();
            $key    = $widgetObject->genCacheKey($setting);
            $xcResult = xc_get($prefix, $key);
            if ($xcResult === false)
            {
                $xcResult = $widgetObject->getView();
            }
            xc_set($prefix, $key, $xcResult, $setting['cache']['time']);
            return $xcResult;
        }
        else
        {
            return $widgetObject->getView();
        }
    }

    /**
     * 将共享变量放到模板变量里
     */
    public function setContext()
    {
        $args = func_get_args();
        $cnt = count($args);
        if ($cnt === 1)
        {
            $this->shareContext = $args[0];
        }
        elseif ($cnt === 2)
        {
            $this->shareContext[$args[0]] = $args[1];
        }
        else
        {
            throw new cls_exception('invalid_share_contenxt_assign');
        }
        return $this;
    }

    public function getContext()
    {
        $args = func_get_args();
        $cnt = count($args);
        if ($cnt === 0)
        {
            return $this->shareContext;
        }
        elseif ($cnt === 1)
        {
            return isset($this->shareContext[$args[0]])
                    ?
                    $this->shareContext[$args[0]]
                    :
                    NULL;
        }
        else
        {
            throw new cls_exception('invalid_args');
        }
    }

    public function setMeta()
    {
        $args   = func_get_args();
        $count  = count($args);
        switch ($count)
        {
            case 1:
                $this->meta = $args[0];
                break;

            case 2:
                $this->meta[$args[0]] = $args[1];
                break;

            default:
                throw new cls_exception('invalid_args');
        }

        return $this;
    }

    public function getMeta()
    {
        $args   = func_get_args();
        $count  = count($args);
        switch ($count)
        {
            case 0:
                return $this->meta;
                break;

            case 1:
                return $this->meta[$args[0]];
                break;

            default:
                throw new cls_exception('invalid_args');
        }
    }

    public function writeMetas()
    {
        foreach ($this->meta as $k => $v)
        {
            echo "<meta name=\"{$k}\" content=\"{$v}\"/>";
        }
    }

    public function setTitle($titleKey, $option = 'APPEND')
    {
        switch ($option)
        {
            case 'APPEND':
            case 'OVERWRITE':
                break;

            default:
                throw new cls_exception('invalid_args');
        }

        $this->titles[] = array('titleKey' => $titleKey, 'option' => $option);
        return $this;
    }

    public function getTitle()
    {
        return $this->shareContext['__page_title__'];
    }

    public function putTitle()
    {
        echo $this->shareContext['__page_title__'];
    }

    private function makeTitle()
    {
        if (substr($GLOBALS['REQUEST_INFO']['url'], -4) === '.php')
        {
            $url = $GLOBALS['REQUEST_INFO']['url'];
        }
        else
        {
            $dispatchFile = substr(strrchr(DISPATCH_FILE, '/'), 1);
            if (empty($GLOBALS['REQUEST_INFO']['query_string']))
            {
                $url = $dispatchFile;
            }
            else
            {
                $url = $dispatchFile . '?' . $GLOBALS['REQUEST_INFO']['query_string'];
            }
        }

        $delimiter = '_';
        $title = array();
        if (count($this->titles) > 0)
        {
            $titles = $this->titles;
            $temp = array_pop($titles);
            if (isset($temp['option']) && $temp['option'] === 'APPEND')
            {
                $title['option'] = 'APPEND';
            }
            else
            {
                $title['option'] = 'OVERWRITE';
            }

            if (isset($temp['titleKey']))
            {
                $title['title'] = $this->lang->get($temp['titleKey']);
            }

            $count = count($titles);
            for ($i = $count - 1; $i >= 0; $i--)
            {
                if (isset($titles[$i]['titleKey']) && isset($titles[$i]['option']))
                {
                    if ($titles[$i]['option'] === 'APPEND')
                    {
                        $title['title'] .= $delimiter . $this->lang->get($titles[$i]['titleKey']);
                    }
                    else
                    {
                        $title['title'] = $this->lang->get($titles[$i]['titleKey']);
                    }
                }
            }
        }

        if (empty($title['title']))
        {
            if (empty($GLOBALS['TITLE_CONFIGS'][Framework::$lang][$url]))
            {
                esq_logs('no_title.log', $GLOBALS['REQUEST_INFO']['domain'] . '/' . $url);
                throw new cls_exception('标题未设置');
            }
            else
            {
                $title = array('title' => '');
            }
            $title['option'] = 'APPEND';
        }

        $titles = array($title['title']);
        if (!empty($GLOBALS['TITLE_CONFIGS'][Framework::$lang][$url]))
        {
            if ($title['option'] === 'APPEND')
            {
                $titles[] = $GLOBALS['TITLE_CONFIGS'][Framework::$lang][$url];
            }
        }

        if (!empty($GLOBALS['SERVICE_CONFIG']['name']))
        {
            $titles[] = $GLOBALS['SERVICE_CONFIG']['name'];
        }

        $sitename = Framework::get_sitename_setting();
        if ($sitename !== '')
        {
            $titles[] = $sitename;
        }

        $indexpage_key = NULL;
        foreach ($titles AS $key => $value)
        {
            if ($value === '')
            {
                unset($titles[$key]);
            }
            elseif ($value === '首页' && $url === 'index.php')
            {
                $indexpage_key = $key;
            }
            elseif ($indexpage_key !== NULL)
            {
                unset($titles[$indexpage_key]);
                $titles[$key] = $value . '首页';

                $indexpage_key = NULL;
            }
        }

        return htmlspecialchars(implode($delimiter, $titles), ENT_QUOTES, 'UTF-8');
    }

    public function setStyleFactor($var)
    {
        $this->styleFactor = $var;
        return $this;
    }

    public function getStyleFactor()
    {
        return $this->styleFactor;
    }

    public function init($page, $contentType = Template::DEFAULT_CONTENT_TYPE)
    {
        $this->initPage($page, $contentType);
        $this->prepareOutput();
    }

    public function initPage($page, $contentType = Template::DEFAULT_CONTENT_TYPE)
    {
        $this->pageName     = $page;
        $this->contentType  = $contentType;

        self::$entireContainerSettings[$this->pageName]
            = PageUtil::buildContainerDefaultSettings($this->pagePath, $this->pageName);

        return $this;
    }

    public function prepareOutput()
    {
        require_library('framework.lang');

        $this->lang = cls_lang::get_instance();
        $this->lang->appendAppLang($this->project,  $this->app);
        $this->lang->appendPageLang($this->project, $this->app, $this->pageName);

        $this->setContext('__page_title__', $this->makeTitle());

        $this->assign('editMode', $this->editMode);
        $this->assign('lang',     $this->lang);

        $this->fullPathTemplateFile = $this->pagePath . '/' . $this->pageName . self::PAGE_TEMPLATE_SUFFIX;

        $this->fillContainers($this->editMode === true ? $this->buildContainersForEdit() : $this->buildContainersForView());

        return $this;
    }

    private function editorScripts()
    {
        require_library('framework.Widget');

        $usedWidget = array();
        foreach (self::$entireContainerSettings as $containerSettings)
        {
            foreach ($containerSettings as $setting)
            {
                $usedWidget = array_merge($usedWidget, $setting['allowedWidgets']);
            }
        }

        $usedWidget = array_unique($usedWidget);
        $scripts = array();
        foreach ($usedWidget as $widgetFullName)
        {
            $widgetObject = Widget::loadWidget($widgetFullName);
            $flag = $widgetObject->getScriptFlag();
            if ($flag > 0)
            {
                $fullPath = Widget::getWidgetPath($widgetFullName);
                $path = substr($fullPath, strlen(ROOT_PATH)) . '/scripts/';

                if ($flag & Widget::HAS_EDITOR_SCRIPT)
                {
                    $scripts[] = $path . 'editor.js';
                }

                if ($flag & Widget::HAS_LANG_SCRIPT)
                {
                    $scripts[] = $path . 'lang/' . Framework::$lang . '.js';
                }
            }
        }

        if (empty($scripts))
        {
            return '';
        }
        else
        {
            static $domain = NULL;
            if ($domain === NULL)
            {
                $domain = get_domain_name_by_app('theme', true);
            }

            return $domain . implode(',', $scripts);
        }
    }

    private function genRandId()
    {
        $args = func_get_args();
        if (count($args) == 0)
        {
            return PageUtil::genRandId();
        }
        else
        {
            return md5(serialize($args));
        }
    }
}

?>